--[[
Name: unitDoodles
Author: Sonora (The Dragonflight, US Kirin Tor)
Website & SVN: http://code.google.com/p/browniehelper/source/browse/#svn/trunk/unitDoodles
License: GNU General Public License v3
]]

--Runs in application namespace
setfenv(1, unitDoodles)

--[[
Doodle Classes

Each doodle needs 3 things:

1)  A configuration which defines doodle parameters.  This should be fully 
serializable and ameanable to configuration through in-game UI.  All required
doodle paramters should be defined as doodleDefaults entries in config.lua.

2)  A "builder" that creates the UI elements for the doodle.  The framework is
responsible for subsequently positioning the root UI element on the screen.

3)  A "handler" that is called by the framework after a listener is registered.  
By default handlers are called when the aura on their associated units changes, 
but builders can be used to register handler calls for other events.  A handler 
takes nothing but self (which is the doodle instance; a UI object with doodle 
decoration), and the return is ignored.

]]--

--Abstract doodles
doodles = {}

--Put unusuable doodles in doodles.abstract (all others will be exposed through the UI configuration)
doodles.abstract = {
    
    --A single colored dot texture
    Dot = {
        builder = function(self, unitID, parentFrame)
            --Setup the doodle as a texture object
            --local newDoodle = parentFrame:CreateTexture(nil, 'OVERLAY')
        
            local newDoodle = CreateFrame("Frame", nil, parentFrame)
            newDoodle.texture = newDoodle:CreateTexture()
            newDoodle.texture:SetAllPoints(newDoodle)
        
            newDoodle.unitID = unitID
            
            newDoodle.texture:SetTexture(self.texture)
            newDoodle.texture:SetVertexColor(unpack(self.missingColor))
            
            if self.blendMode then newDoodle.texture:SetBlendMode(self.blendMode) end
            if self.frameLevel then newDoodle:SetFrameLevel(self.frameLevel) end
            
            newDoodle:Show()
            
            return newDoodle
        end,
    
    },
    
    --A single colored status bar
    Bar = {
        builder = function(self, unitID, parentFrame)
            --Setup the doodle as a StatusBar frame
            local newDoodle = CreateFrame('StatusBar', nil, parentFrame)
            newDoodle.unitID = unitID
        
            if self.direction == HORIZONTAL then
                self.width = 0
            else
                self.height = 0
            end
        
            newDoodle:SetOrientation((self.direction or HORIZONTAL))
            newDoodle:SetStatusBarTexture(self.texture)
            newDoodle:SetStatusBarColor(unpack(self.barColor))
            
            if self.backdrop then
                newDoodle:SetBackdrop(self.backdrop)
                newDoodle:SetBackdropColor(0.15, 0.15, 0.15)  --TODO:  make this configurable
            end
            
            newDoodle:Hide()
            
            return newDoodle
        end,
    
    }
}

--Dot and Bar doodles that watch their unit's aura
local function showAuraTooltip(doodle)

    local auraInfo = queryAura(doodle.unitID, doodle.aura)
    GameTooltip_SetDefaultAnchor( GameTooltip, UIParent )
    
    if auraInfo then
        GameTooltip:SetUnitAura(doodle.unitID, auraInfo.auraIndex, auraInfo.filter)
        GameTooltip:Show()
    else
        GameTooltip:AddLine(doodle:getDescription())
        GameTooltip:Show()
    end
        
end

doodles.aura = {

    Dot = {
        
        --Implementation
        inherits = doodles.abstract.Dot,
        mouseOver = showAuraTooltip,
        getDescription = function(self) return self.aura..".dot" end,
        builder = function(self, unitID, parentFrame)
            --If our aura attribute is not set, register an error
            assert(self.aura, 'unitDoodles load failure:  AuraDots must have .aura attributes set')
            
            --Do parental initialization
            local newDoodle = doodles.abstract.Dot.builder(self, unitID, parentFrame)

            --Also register us for Aura update events; framework calls .handler
            addAuraListener(newDoodle)

            --Don't forget to return the new doodle!
            return newDoodle
            
        end,
    
        handler = function(self)
            --[[
            Default behavior is to color dot .presentColor if the aura is there
            and color .missingColor if it is not.
            ]]--
        
            local queryResult = queryAura(self.unitID, self.aura)
            if queryResult then
                --We have the aura
                if not self.onlyShowMine or queryResult.isMine then
                    self.texture:SetVertexColor(unpack(self.presentColor))
                end

            else
                --We don't have the aura
                self.texture:SetVertexColor(unpack(self.missingColor))
            end
        
        end,
    },
    
    Bar = {
        
        --Implementation
        inherits = doodles.abstract.Bar,
        mouseOver = showAuraTooltip,
        getDescription = function(self) return self.aura..".bar" end,
        builder = function(self, unitID, parentFrame)
            --If our aura attribute is not set, register an error
            assert(self.aura, 'unitDoodles load failure:  Aura bars must have .aura attributes set')
            
            --Do parental initialization
            local newDoodle = doodles.abstract.Bar.builder(self, unitID, parentFrame)

            --Also register us for Aura update events; framework calls handler
            addAuraListener(newDoodle)

            --Don't forget to return the new doodle!
            return newDoodle
            
        end,
    
        handler = function(self)
            --[[
            Default behavior is simply to make the bar count down with the time
            left on the aura.
            ]]--
        
            local queryResult = queryAura(self.unitID, self.aura)
            
            --If it was found and its ours or we don't care
            if queryResult and (queryResult.isMine or not self.onlyShowMine) then
                
                --Set things up on the first call
                if not self:IsShown() then 
                    self:Show()
                    self:SetMinMaxValues(0, queryResult.duration)
            
                    --Create a reactor call to update the bar state over time
                    self.reactorCall = function()
                        local result = queryAura(self.unitID, self.aura)
                        if result then self:SetValue(result.expirationTime-GetTime()) end
                    end
            
                    --TODO: it would be much better if this were in framework code
                    if self.stackPosition then
                        updateDoodlePositions(self.unitID)
                    end
            
                    reactor:registerCall(self.reactorCall)
                end
            
            else
                if self:IsShown() then
                    reactor:unregisterCall(self.reactorCall)
                    self:Hide()
            
                    --TODO: it would be much better if this were in framework code
                    if self.stackPosition then
                        updateDoodlePositions(self.unitID)
                    end
            
                end
            end
    
        end,
    
    }
}

--Fancier dots and bars
doodles.aura.debuffHighlight = {
    --[[
    TODO:
    ]]--
    
    --Implementation
    aura = "debuffHighlight",
    inherits = doodles.aura.Dot,
    handler = function(self)
        --[[
        ]]--
    
        for i = 1,40 do
            
            name, rank, icon, count, debuffType = UnitDebuff(self.unitID, i, true)
            if debuffType then
                self.aura = name
                self.texture:SetVertexColor(unpack(self.debuffHighlightColors[debuffType]))
                return true
            end
        end
    
        self.texture:SetVertexColor(0,0,0,0)
    
    end,

}

doodles.aura.BarColoredByStacks = {
    --[[
    Like the normal timer bar, but the color of the bar is adjusted based on the
    number of stacks of the monitored aura.  Useful for things like Life Bloom
    or Molten Core.
    ]]--
    
    --Implementation
    inherits = doodles.aura.Bar,
    
    builder = function(self, unitID, parentFrame)
        --If our possibleStacks attribute is not set, register an error
        assert(self.possibleStacks, 'unitDoodles load failure:  BarColoredByStacks must have a .possibleStacks attribute set')

        --Do parental initialization
        local newDoodle = doodles.aura.Bar.builder(self, unitID, parentFrame)

        --Don't forget to return the new doodle!
        return newDoodle
        
    end,
    
    handler = function(self)
        --[[
        We color the timer bar based on the number of stacks by interpolating
        between the start color (.barColor) and the full stack color 
        (.fullStackBarColor) based on the number of .possibleStacks.
        ]]--
    
        --Call the parental version of this function to set the state of the bar
        doodles.aura.Bar.handler(self)

        --If after that we're being shown, color the bar based on the stacks
        if self:IsShown() then
    
            local interpolatedBarColor = {}
            for i,v in ipairs(self.barColor) do
                
                --Find the value difference between the one stack and full stack
                local result = queryAura(self.unitID, self.aura)
                local valDelta = abs(self.fullStackBarColor[i] - v)
                local step = valDelta / (self.possibleStacks-1)
                local adjustment = step * (result.count-1)
                
                --Figure out if we need to add or substract from bar color
                if v < self.fullStackBarColor[i] then
                    --Dark to light
                    table.insert(interpolatedBarColor, v + adjustment)
                else
                    --Light to dark
                    table.insert(interpolatedBarColor, v - adjustment)
                end
                
            end
            self:SetStatusBarColor(unpack(interpolatedBarColor))
        
        end
    
    end,

}

--Setup inheritance among the doodles
for category, doodleSet in pairs(doodles) do
    for doodleName, doodle in pairs(doodleSet) do
        setupInheritance(doodle)
    
        --TODO: remove debug code
        doodle.name = doodleName
    end
end
